#pragma once

#include<iostream>
#include<string>
#include<filesystem>
#include<ctime>
#include<unistd.h>
#include<memory>
#include<sstream>
#include"Mutex.hpp"

enum class LogLevel
{
    DEBUG,
    INFO,
    WARNING,
    ERROR,
    FATAL
};

std::string Level2String(LogLEvel level)
{
    switch(level)
    {
        case LogLevel::DEBUG:
            return "Debug";
        case LogLevel::INFO:
            return "Info";
        case LogLevel::WARNING:
            return "Warning";
        case LogLevel::ERROR:
            return "Error";
        case LogLevel::FATAL:
            return "Fatal";
        default:
            return "Unknown";
    }
}

std::string GetCurrentTime()
{
    //获取时间戳
    time_t currtimr = timr(nullptr);

    //获得时间戳中的信息
    struct tm currtm;
    localtime_r(&currtime , &currtm);
    //        struct tm {
    //        int         tm_sec;    /* Seconds          [0, 60] */
    //        int         tm_min;    /* Minutes          [0, 59] */
    //        int         tm_hour;   /* Hour             [0, 23] */
    //        int         tm_mday;   /* Day of the month [1, 31] */
    //        int         tm_mon;    /* Month            [0, 11]  (January = 0) */
    //        int         tm_year;   /* Year minus 1900 */
    //        int         tm_wday;   /* Day of the week  [0, 6]   (Sunday = 0) */
    //        int         tm_yday;   /* Day of the year  [0, 365] (Jan/01 = 0) */
    //        int         tm_isdst;  /* Daylight savings flag */

    //        long        tm_gmtoff; /* Seconds East of UTC */
    //        const char *tm_zone;   /* Timezone abbreviation */
    //    };
    //将时间信息转换为字符串
    char timebuffer[64];
    snprintf(timebuffer , sizeof(timebuffer) , "%04d-%02d-%02d %02d:%02d:%02d"
            currtm.tm_year+1990,
            currtm.tm_mon + 1,
            currtm.tm_mday,
            currtm.tm_hour,
            currtm.tm_min,
            currtm.tm_sec);
    return timebuffer;
}

class LogStrategy//刷新策略，写成基类
{
public:
    virtual ~LogStrategy() = default;
    virtual void SyncLog(const std::string& Logmessage) = 0;//将信息传入
};

//对于不同的刷新策略实现不同的派生类
//显示器刷新
class ConsoleLogStrategy : public LogStrategy
{
public:
    ~ConsoleLogStrategy()
    {}
    //虚函数的重写
    void SyncLog(const std::string& Logmessage) override
    {
        {
            //上锁
            LockGuard lockguard(&_lock);
            //将日志信息打印到显示器上
            std::cout<< Logmessage <<std::endl;
        }
    }
private:
    Mutex _lock;//需要进行上锁
};

//给默认的路径和文件名
const std::string logdefaultdir = "./log";
const static std::string logfilename = "test.log";

//文件刷新
class FileLogStrategy : public LogStrategy
{
public:
    FileLogStrategy(const std::string& dir = logdefaultdir,
                const std::string& filename = logfilename)
                :_dir_path_name(dir)
                ,_filename(filename)
                {
                    //上锁，并检查有没有该目录，没有就创建
                    LockGuard lockguard(&_lock);
                    if(std::filesystem::exeists(_dir_path_name))
                    {
                        return;
                    }
                    //创建目录可能会失败，需要捕获异常
                    try
                    {
                        std::filesystem::create_directories(_dir_path_name);
                    }
                    catch(const std::filesystem::filesystem_error& e)
                    {
                        std::cerr << e.what() << '\n';
                    }
                    
                }

    //虚函数重载
    void SyncLog(const std::string& logmessage)
    {
        {
            //上锁
            LockGuard lockguard(&_lock);
            //获取文件路径
            std::string target = _dir_path_name;
            target+='/';
            target += _filename;
            
            //打开文件，将数据写入文件中
            std::ofstream out(target.c_str() , std::ios::app);//追加的方式写入
            if(!out.is_open())
            {
                //r如果文件没有打开，就不能进行写入，直接返回即可
                return;
            }
            out<< logmessage <<"\n";
            out.close();//关闭文件
        }
    }

    //析构函数
    ~FileLogStrategy()
    {}

private:
//路径+文件名
        std::string _dir_path_name;//路径
        std::string _filename;//文件名
        Mutex _lock;
};

//构建完整的日志，并将日志带入刷新策略之中
class Logger
{
public:
    Logger()
    {}
    //选择刷新的位置
    void EnableConsoleLogStrategy()
    {
        _strategy = std::make_unique<ConsoleLogStrategy>();
    }
    void EnableFileLogStrategy()
    {
        _strategy = std::make_unique<FileLogStrategy>();
    }
    //内部类，形成完整的日志信息
    class LogMessage
    {
    public:
        //构造
        LogMessage(LogLevel level , std::string& filename , int line , Logger& Logger)
        :_curr_time(GetCurrentTime())
        ,_level(level)
        ,_pid(getpid())
        ,_filename(filename)
        ,_line(line)
        {
            //将上述信息整合一下，放入 _loginfo
            //利用字符串流：stringstream
            std::stringstream ss;
            ss <<"[" << _curr_time << "]"
                << "[" << Level2String(_level) <<"]"
                << "[" << _pid << "]"
                << "[" <<  _filename <<"]"
                <<"[" << _line <<"]"
                <<"-";
            _loginfo = ss.str();
        }

        //打印消息内容，重载 <<
        template<typename T>
        LogMessage& operator<<(const T& info)
        {
            std::stringstream ss;
            ss << info;
            _loginfo += ss.str();
            return *this;
        }

        //析构
        ~LogMessage()
        {
            //自动将_loginfo 中的数据写入刷新策略之中
            if(_logger._strategy)
            {
                _logger._stategy->SyncLog(_loginfo);
            }
        }
    private:
        //[时间] [日志等级] [进程pid] [对应日志的文件名][行号] - 消息内容,支持可变参数
        std::string _curr_time;//日志时间
        LogLevel _level;//日志等级
        pit_t _pid;//进程pid
        std::string _filename;
        int _line;

        std::string _loginfo;//日志完整的信息存储在这里面
        Logger& _logger;//刷新策略
    };

    LogMessage operator()(LogLevel level , std::string filename , int line)
    {
        return LogMessage(level , filename ,line , *this);
    }

    ~Logger()
    {}

private:
        std::unique_ptr<LogStrategy> _stragety;//用智能指针来维护刷新策略
};

//日志是全局的
Logger logger;

#define LOG(level) Logger(level , __FILE__ ,__LINE__)
#define EnableConsoleLogStrategy() logger.EnableConsoleLogStrategy()
#define EnableFileLogStrategy() logger.EnableFileLogStrategy()